package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.aliyun.iot20180120.Client;
import com.aliyun.iot20180120.models.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.zzyl.base.PageResponse;
import com.zzyl.base.ResponseResult;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.DeviceDto;
import com.zzyl.dto.DevicePageQueryDto;
import com.zzyl.entity.Device;
import com.zzyl.enums.BasicEnum;
import com.zzyl.enums.DeviceLocationTypeEnum;
import com.zzyl.enums.DevicePhysicalLocationTypeEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.properties.AliIoTConfigProperties;
import com.zzyl.service.DeviceService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.vo.DeviceVo;
import com.zzyl.vo.ProductVo;
import org.apache.coyote.Request;
import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description DeviceServiceImpl
 * @Author chendongping
 * @Date 2025-05-19  18:34
 */
@Service
/**
 * 设备服务实现类，提供与阿里云物联网平台交互的功能
 * 包括获取设备数据、添加设备、查询设备详情等
 */
public class DeviceServiceImpl implements DeviceService {

    // 注入阿里云客户端，用于发送请求到阿里云物联网平台
    @Autowired
    private Client client;

    // 注入阿里云IoT配置属性，包含阿里云物联网平台的相关配置
    @Autowired
    private AliIoTConfigProperties  aliIoTConfigProperties;

    // 注入Redis模板，用于操作Redis缓存
    @Autowired
    private RedisTemplate<String,String>  redisTemplate;

    // 注入设备映射器，用于操作设备相关的数据库表
    @Autowired
    private DeviceMapper deviceMapper;

    /**
     * 从阿里云物联网平台获取所有设备数据，并存储到Redis缓存中
     * 先构造查询产品列表的请求，然后调用阿里云物联网平台的API获取产品列表，
     * 并将获取到的产品信息转换后存储到Redis缓存中
     */
    @Override
    public void getAllDeviceData() {
        //构造查询产品列表的请求
        QueryProductListRequest queryProductListRequest = new QueryProductListRequest();
        queryProductListRequest.setCurrentPage(1);
        queryProductListRequest.setPageSize(100);
        queryProductListRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());

        QueryProductListResponse queryProductListResponse = null;
        try {
             //发送请求，获取产品列表
             queryProductListResponse = client.queryProductList(queryProductListRequest);
        } catch (Exception e) {
            //如果发生异常，抛出自定义异常
            throw new BaseException(BasicEnum.IOT_LIST_PRODUCT_ERROR);
        }
        //如果请求成功，但返回结果不成功，也抛出自定义异常
        if (!queryProductListResponse.getBody().getSuccess()) {
            throw new BaseException(BasicEnum.IOT_LIST_PRODUCT_ERROR);
        }

        //处理返回结果，提取产品信息，并转换为ProductVo对象列表
        List<ProductVo> productVoList = queryProductListResponse.getBody().getData().getList()
                .getProductInfo().stream().map(p -> BeanUtil.toBean(p, ProductVo.class)).collect(Collectors.toList());
        //将产品信息列表转换为JSON字符串，并存储到Redis缓存中
        redisTemplate.opsForValue().set(CacheConstants.ALL_PRODUCT_KEY, JSONUtil.toJsonStr(productVoList));
    }

    /**
     * 从Redis缓存中获取所有产品数据
     * 此方法首先尝试从Redis缓存中获取产品数据的JSON字符串，
     * 如果获取失败则返回空集合，否则将JSON字符串转换为产品对象列表并返回
     *
     * @return 产品对象列表，如果缓存中没有数据则返回空集合
     */
    @Override
    public List<ProductVo> allProduct() {
        //从缓存中获取数据
        String productListJsonStr = redisTemplate.opsForValue().get(CacheConstants.ALL_PRODUCT_KEY);
        //如果为空，返回空集合
        if(ObjectUtil.isEmpty(productListJsonStr)){
            return Collections.emptyList();
        }
        //转换数据，并返回
        return JSONUtil.toList(productListJsonStr,ProductVo.class);
    }

    /**
     * 添加新设备
     * 首先检查设备名称是否已存在，然后通过阿里云物联网平台注册设备，
     * 并将设备信息保存到数据库中
     *
     * @param deviceDto 设备数据传输对象，包含设备的详细信息
     */
    @Override
    public void addDevice(DeviceDto deviceDto) {
        //检验设备名称是否重复
        Long count = deviceMapper.countByDeviceName(deviceDto.getDeviceName());
        if(count > 0){
            throw new BaseException(BasicEnum.DEVICE_NAME_EXIST);
        }
        //iot新增设备
        RegisterDeviceRequest registerDeviceRequest = deviceDto.getRegisterDeviceRequest();
        registerDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        RegisterDeviceResponse response = null;
        try {
            response = client.registerDevice(registerDeviceRequest);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        //接口是否调通
        if(!response.getBody().getSuccess()){
            throw new RuntimeException(response.getBody().getErrorMessage());
        }

        //根据productKey调用阿里云查询产品信息，获取productName
        QueryProductRequest queryProductRequest = new QueryProductRequest();
        queryProductRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        queryProductRequest.setProductKey(deviceDto.getProductKey());
        QueryProductResponse queryProductResponse = null;
        try {
            queryProductResponse = client.queryProduct(queryProductRequest);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_PRODUCT_ERROR);
        }

        //接口是否调通
        if(!queryProductResponse.getBody().getSuccess()){
            throw new RuntimeException(response.getBody().getErrorMessage());
        }

        //保存设备
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        device.setIotId(response.getBody().getData().getIotId());
        device.setProductName(queryProductResponse.getBody().getData().getProductName());
        //如果设备绑定的位置是随身设备，则把物理绑定位置设置为-1
        if(device.getLocationType().equals(DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal())){
            device.setPhysicalLocationType(DevicePhysicalLocationTypeEnum.ELDER.getOrdinal());
        }
        deviceMapper.insertSelective(device);
    }

    /**
     * 分页查询设备
     * 使用MyBatis的分页插件PageHelper进行分页查询，并将结果转换为DeviceVo对象列表
     *
     * @param devicePageQueryDto 设备分页查询数据传输对象，包含查询条件和分页信息
     * @return 分页响应对象，包含设备列表和分页信息
     */
    @Override
    public PageResponse<DeviceVo> pageQueryDevice(DevicePageQueryDto devicePageQueryDto) {
        PageHelper.startPage(devicePageQueryDto.getPageNum(),devicePageQueryDto.getPageSize());
       Page<DeviceVo> devicevos = deviceMapper.pageQueryDevice(devicePageQueryDto);
        return PageResponse.of(devicevos,DeviceVo.class);
    }

    /**
     * 查询设备详情
     * 通过调用阿里云物联网平台的API获取设备详情，并将结果与数据库中的设备信息合并后返回
     *
     * @param queryDeviceDetailRequest 查询设备详情请求对象，包含设备的标识信息
     * @return 设备详情对象，包含设备的详细信息
     */
    @Override
    public DeviceVo queryDeviceDetail(QueryDeviceDetailRequest queryDeviceDetailRequest) {
        // 设置阿里云物联网平台实例ID
        queryDeviceDetailRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryDeviceDetailResponse response = null;
        try {
            // 调用阿里云物联网平台API查询设备详情
            response = client.queryDeviceDetail(queryDeviceDetailRequest);
        } catch (Exception e) {
            // 如果查询过程中发生异常，则抛出自定义异常
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        // 如果查询结果不成功，则抛出运行时异常，包含错误信息
        if (!response.getBody().getSuccess()) {
            throw new RuntimeException(response.getBody().getErrorMessage());
        }
        // 从数据库中获取设备信息
        DeviceVo deviceVo = deviceMapper.selectByIotId(queryDeviceDetailRequest.getIotId());
        // 将查询到的设备详情与数据库中的设备信息合并
        BeanUtil.copyProperties(response.getBody().getData(), deviceVo);
        return deviceVo;
    }


    /**
     * 查询已发布的物模型
     *
     * @param request 查询已发布物模型的请求对象，包含查询所需的信息
     * @return 返回包含查询结果的响应对象，包括物模型数据
     * @throws BaseException 当查询操作失败时抛出基础异常
     * @throws RuntimeException 当响应结果不成功时抛出运行时异常
     */
    @Override
    public ResponseResult<QueryThingModelResponseBody.QueryThingModelResponseBodyData> queryThingModelPublished(QueryThingModelPublishedRequest request) {
        // 设置请求中的IoT实例ID
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryThingModelPublishedResponse response = null;
        try {
            // 调用客户端方法查询已发布的物模型
            response = client.queryThingModelPublished(request);
        } catch (Exception e) {
            // 当查询过程中发生异常时，抛出自定义基础异常
            throw new BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }
        // 如果响应结果不成功，抛出运行时异常，包含错误信息
        if(!response.getBody().getSuccess()){
            throw new RuntimeException(response.getBody().getErrorMessage());
        }
        // 返回包含查询结果的响应对象
        return ResponseResult.success(response.getBody().getData());
    }

    /**
     * 查询设备属性状态
     * 该方法用于查询阿里云物联网平台上的设备属性状态它通过调用阿里云物联网平台的API
     * 来获取设备的当前属性状态信息，并处理可能的错误情况
     * @param request 包含查询请求参数的对象，包括设备信息和需要查询的属性等
     * @return 返回一个包含设备属性状态信息的响应结果对象如果查询成功，
     *         则响应结果对象中会包含设备的属性状态数据如果查询失败，
     *         则会抛出异常
     * @throws BaseException 如果查询过程中发生错误，抛出自定义的基础异常
     */
    @Override
    public ResponseResult<QueryDevicePropertyStatusResponseBody.QueryDevicePropertyStatusResponseBodyData> queryDevicePropertyStatus(QueryDevicePropertyStatusRequest request) {
        // 设置请求中的物联网实例ID，从配置属性中获取
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());

        // 初始化响应对象
        QueryDevicePropertyStatusResponse response = null;

        try {
            // 调用客户端方法查询设备属性状态
            response = client.queryDevicePropertyStatus(request);
        } catch (Exception e) {
            // 如果查询过程中发生异常，抛出自定义异常
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_PROPERTY_STATUS_ERROR);
        }

        // 如果响应体中的成功标志为false，抛出运行时异常，包含错误消息
        if(!response.getBody().getSuccess()){
            throw new RuntimeException(response.getBody().getErrorMessage());
        }

        // 如果查询成功，返回包含设备属性状态数据的成功响应结果
        return ResponseResult.success(response.getBody().getData());
    }



    /**
     * 修改设备
     */
    @Override
    public void updateDevice(DeviceDto deviceDto) {
        Device deviceId = deviceMapper.selectByPrimaryKey(deviceDto.getId());
        if (ObjectUtil.isEmpty(deviceId)){
            throw new BaseException(BasicEnum.DEVICE_NOT_EXIST);
        }
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        if (device.getLocationType().equals(DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal())){
            device.setLocationType(DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal());
        }
        deviceMapper.updateByPrimaryKeySelective(device);
        //调用物联网接口，更新云服务设备信息，这里只会更新阿里云那边的昵称，产品分类和设备名称一旦创建不允许再修改
        BatchUpdateDeviceNicknameRequest request = new BatchUpdateDeviceNicknameRequest();
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo nicknameInfo = BeanUtil.toBean(deviceDto, BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo.class);
        request.setDeviceNicknameInfo(Lists.newArrayList(nicknameInfo));
        BatchUpdateDeviceNicknameResponse response = null;
        try {
            response = client.batchUpdateDeviceNickname(request);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
        }
        if (!response.getBody().getSuccess()) {
            throw new RuntimeException(response.getBody().getErrorMessage());
        }
    }

    @Override
    public void deleteDevice(DeleteDeviceRequest request) {
        deviceMapper.deleteDevice(request.getIotId());
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        DeleteDeviceResponse response = null;
        try {
            response = client.deleteDevice(request);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
        }
        if (!response.getBody().getSuccess()) {
            throw new RuntimeException(response.getBody().getErrorMessage());
        }
    }
}


